﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IPIdee.MapServices.Client.Entities.Coordinates
{
    public static class CoordinateConvertor
    {
        public static int GetAbsoluteYOnZoom(double latitude, int zoom)
        {
            int tileY = GetTileYOnZoom(latitude, zoom);
            int mapspotY = GetMapspotYOnZoom(latitude, zoom);

            return GetAbsoluteYByRelativeCoordinateElements(tileY, mapspotY);
        }

        public static int GetAbsoluteXOnZoom(double longitude, int zoom)
        {
            int tileX = GetTileXOnZoom(longitude, zoom);
            int mapspotX = GetMapspotXOnZoom(longitude, zoom);
            return GetAbsoluteXByRelativeCoordinateElements(tileX, mapspotX);
        }

        public static int GetAbsoluteXByRelativeCoordinateElements(int tileX, int mapspotX)
        {
            return tileX * 16 + mapspotX;
        }

        public static int GetAbsoluteYByRelativeCoordinateElements(int tileY, int mapspotY)
        {
            return tileY * 16 + mapspotY;
        }

        public static int GetTileYOnZoom(double lati, int zoom)
        {
            double maxlat = Math.PI;

            double lat = (double)lati;

            if (lat > 90)
            {
                lat = lat - 180;
            }
            if (lat < -90)
            {
                lat = lat + 180;
            }

            // conversion degre=>radians
            double phi = Math.PI * lat / 180;
            double res;
            res = 0.5 * Math.Log((1 + Math.Sin(phi)) / (1 - Math.Sin(phi)));
            double maxTileY = Math.Pow(2, zoom);
            double yValue = (double)(((1 - res / maxlat) / 2) * (maxTileY));

            int result = (int)Math.Floor(yValue);

            return result;
        }

        public static int GetMapspotYOnZoom(double lati, int zoom)
        {
            double maxlat = Math.PI;
            double lat = (double)lati;
            if (lat > 90) lat = lat - 180;
            if (lat < -90) lat = lat + 180;
            double phi = Math.PI * lat / 180;
            double res;
            res = 0.5 * Math.Log((1 + Math.Sin(phi)) / (1 - Math.Sin(phi)));
            double maxTileY = Math.Pow(2, zoom);
            double yValue = (double)(((1 - res / maxlat) / 2) * (maxTileY));
            int tileY = GetTileYOnZoom(lati, zoom);
            int result = (int)((yValue - tileY) * 16);
            return result;
        }

        public static int GetTileXOnZoom(double longi, int zoom)
        {
            // longitude van -180 tot 180
            // latitide van -90 tot 90
            double lon = (double)longi;
            var maxTiles = (int)Math.Pow(2, zoom);
            var xValue = maxTiles * ((lon + 180) / 360);
            int result = (int)Math.Floor(xValue);
            return result;
        }

        public static int GetMapspotXOnZoom(double longi, int zoom)
        {
            double lon = (double)longi;

            // longitude van -180 tot 180
            // latitide van -90 tot 90

            var maxTiles = (int)Math.Pow(2, zoom);
            var xValue = maxTiles * ((lon + 180) / 360);

            int tileX = GetTileXOnZoom(longi, zoom);

            int result = (int)((xValue - tileX) * 16);
            return result;
        }

        public static double GetLongitudeByAbsoluteXOnZoom(int absoluteX, int zoom)
        {
            // er zitten 16 mapspots in een tile, vandaar even delen door 16, werk met het heel en restdeel
            double absX = ((double)absoluteX) / 16;

            var maxTilesX = (int)Math.Pow(2, zoom);

            double x = (absX / maxTilesX) - 0.5;
            double longitude = 360 * x;

            return (double)longitude;
        }

        public static double GetLatitudeByAbsoluteYOnZoom(int absoluteY, int zoom)
        {
            double absY = ((double)absoluteY) / 16;

            double maxTileY = Math.Pow(2, zoom);
            double y = 0.5 - (absY / maxTileY);

            double d = 90 - 360 * Math.Atan(Math.Exp(-y * 2 * Math.PI)) / Math.PI;

            return (double)d;
        }
    }
}
